package cn.virens.config;

import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Properties;

import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.beetl.ext.spring.BeetlSpringViewResolver;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.MediaType;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.ResourceHttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.alibaba.fastjson.support.spring.FastJsonJsonView;

import cn.virens.common.CalendarUtil;
import cn.virens.web.components.converter.DateConverter;
import cn.virens.web.components.spring.UniversallyExceptionResolver;
import cn.virens.web.components.spring.exception.NotFileFoundException;

/**
 * Spring MVC 配置
 * 
 * @文件   :SpringMvcConfig.java
 * @作者   :cn.virens
 * @创建时间 :2016年10月13日 下午1:12:09
 */
@EnableWebMvc
@Configuration
@ComponentScan("cn.virens.web.controller")
@Import(value = { SpringBeetlConfig.class, SpringWebSocketConfig.class, SpringWeixinMpConfig.class })
public class ServletWebConfig implements WebMvcConfigurer {
	private static final Charset charset = Charset.forName("UTF-8");

	@Value("${mvc.exception}")
	private boolean showException;

	/**
	 * 内容协商器配置。将后缀为json的配置为json处理，后缀为jspx的为页面处理
	 */
	@Override
	public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
		configurer.defaultContentType(MediaType.APPLICATION_JSON_UTF8);

		configurer.mediaType("jspx", MediaType.TEXT_HTML);
		configurer.mediaType("json", MediaType.APPLICATION_JSON_UTF8);
	}

	/**
	 * 添加视图解析器
	 */
	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		// 配置Beetl 视图
		BeetlSpringViewResolver beetlSpringViewResolver = new BeetlSpringViewResolver();
		beetlSpringViewResolver.setContentType("text/html;charset=UTF-8");
		beetlSpringViewResolver.setSuffix(".html");
		beetlSpringViewResolver.setOrder(2);

		// 配置FastJson 视图
		FastJsonJsonView fastJsonJsonView = new FastJsonJsonView();
		fastJsonJsonView.setFastJsonConfig(fastJsonConfig());

		// 注册视图
		registry.viewResolver(beetlSpringViewResolver);
		registry.enableContentNegotiation(fastJsonJsonView);
	}

	/**
	 * 添加消息转换器
	 */
	@Override
	public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
		FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
		fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig());

		// 将消息转换器转给Spring进行管理
		converters.add(new ByteArrayHttpMessageConverter());
		converters.add(new ResourceHttpMessageConverter());
		converters.add(new SourceHttpMessageConverter<>());
		converters.add(new StringHttpMessageConverter(charset));
		converters.add(fastJsonHttpMessageConverter);
	}

	/**
	 * 添加异常统一处理
	 */
	@Override
	public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
		UniversallyExceptionResolver exceptionResolver = new UniversallyExceptionResolver();

		exceptionResolver.addExceptionMessage(UnauthorizedException.class, "未授权");
		exceptionResolver.addExceptionMessage(UnauthenticatedException.class, "未登录");
		exceptionResolver.addExceptionMessage(NoHandlerFoundException.class, "接口不存在");
		exceptionResolver.addExceptionMessage(DataIntegrityViolationException.class, "请检查约束");
		exceptionResolver.addExceptionMessage(MaxUploadSizeExceededException.class, "文件大小超出限制");

		exceptionResolver.addExceptionMapping(AuthorizationException.class, "/error/401");
		exceptionResolver.addExceptionMapping(NoHandlerFoundException.class, "/error/404");
		exceptionResolver.addExceptionMapping(NotFileFoundException.class, "/error/404-file");

		exceptionResolver.setDefaultErrorView("/error/500");
		exceptionResolver.setShowException(showException);

		exceptionResolvers.add(exceptionResolver);
	}

	/**
	 * 格式转换器
	 */
	@Override
	public void addFormatters(FormatterRegistry registry) {
		registry.addConverter(new DateConverter());
	}

	/**
	 * 添加静态资源路径
	 */
	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		registry.addResourceHandler("/assets/**").addResourceLocations("/assets/");
		registry.addResourceHandler("/upload/**").addResourceLocations("/upload/");
		registry.addResourceHandler("/favicon.ico").addResourceLocations("/favicon.ico");
	}

	/**
	 * 跨域配置
	 */
	@Override
	public void addCorsMappings(CorsRegistry registry) {
		registry.addMapping("/api/**").allowedMethods("GET", "POST", "OPTIONS").allowedHeaders("X-Auth-Token", "Content-Type").exposedHeaders("X-Auth-Token").allowCredentials(true).maxAge(86400);
	}

	/** 文件上传配置 */
	@Bean
	public CommonsMultipartResolver multipartResolver() {
		CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
		multipartResolver.setMaxUploadSize(10 * 1024 * 1024);
		multipartResolver.setDefaultEncoding("UTF-8");

		return multipartResolver;
	}

	/** 注入配置文件 */
	@Bean("propertyPlaceholderConfigurer")
	public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer(@Qualifier("propertiesBean") Properties properties) throws IOException {
		PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer();
		configurer.setProperties(properties);

		return configurer;
	}

	private FastJsonConfig fastJsonConfig() {
		FastJsonConfig config = new FastJsonConfig();
		config.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);
		config.setDateFormat(CalendarUtil.YMD_HMS); // 所有时间格式都为yyyy-MM-dd HH:mm:ss
		config.setCharset(charset);

		return config;
	}
}